{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Chapter 6 -  Model Deployment for Time Series Forecasting - Serving"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ## Deployment\n",
    " This script allows you to use the model in a webservice and get the desired results.\n",
    " Once the model is trained, it's possible to deploy it in a service.\n",
    " #### For this you need the following steps:\n",
    " * Retrieve the workspace\n",
    " * Get or register the model\n",
    " * Create a docker image\n",
    " * Create the ACI service\n",
    " * Deploy the service\n",
    " * Test the service"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " Import Azure Machine Learning Python SDK and other modules."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import ast\n",
    "import json\n",
    "import os\n",
    "\n",
    "import azureml.core\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import sklearn\n",
    "from azureml.core import Workspace\n",
    "from azureml.core.environment import Environment\n",
    "from azureml.core.model import InferenceConfig, Model\n",
    "from azureml.core.webservice import AciWebservice\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "\n",
    "from energydemandforecasting.utils import load_data\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ### Retrieve AML workspace\n",
    " The workspace that was used for training must be retrieved."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ws = Workspace.from_config()\n",
    "print(ws.name, ws.resource_group, ws.location, ws.subscription_id, sep=\"\\n\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ### Get or register the model (optional)\n",
    " We already registered the model in the training script.\n",
    " But if the model you want to use is only saved locally, you can uncomment and run the following cell, that will register your model in the workspace.\n",
    " Parameters may need adjustment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# model = Model.register(model_path = \"path_of_your_model\",\n",
    "#                        model_name = \"name_of_your_model\",\n",
    "#                        tags = {'type': \"Time series ARIMA model\"},\n",
    "#                        description = \"Time series ARIMA model\",\n",
    "#                        workspace = ws)\n",
    "\n",
    "# get the already registered model\n",
    "model = Model.list(ws, name=\"arimamodel\")[0]\n",
    "print(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Get or Register an Environment\n",
    "\n",
    "We already registered the environment in the training script."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# my_azureml_env = Environment.from_conda_specification(name = \"my_azureml_env\",\n",
    "#                                                    file_path = \"./energydemandforecasting/azureml-env.yml\")\n",
    "# my_azureml_env.register(workspace=ws)\n",
    "\n",
    "my_azureml_env = Environment.get(workspace=ws, name=\"my_azureml_env\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inference_config = InferenceConfig(\n",
    "    entry_script=\"energydemandforecasting/score.py\", environment=my_azureml_env\n",
    ")\n",
    "\n",
    "# Set deployment configuration\n",
    "deployment_config = AciWebservice.deploy_configuration(cpu_cores=1, memory_gb=1)\n",
    "\n",
    "aci_service_name = \"aci-service-arima\"\n",
    "\n",
    "# Define the model, inference, & deployment configuration and web service name and location to deploy\n",
    "service = Model.deploy(\n",
    "    workspace=ws,\n",
    "    name=aci_service_name,\n",
    "    models=[model],\n",
    "    inference_config=inference_config,\n",
    "    deployment_config=deployment_config,\n",
    ")\n",
    "\n",
    "service.wait_for_deployment(True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ### Call the service and test it\n",
    " The service is tested on the `energy.csv` data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# load the data to use for testing and encode it in json\n",
    "energy_pd = load_data(\"./data/energy.csv\")\n",
    "energy = pd.DataFrame.to_json(energy_pd, date_format=\"iso\")\n",
    "energy = json.loads(energy)\n",
    "energy = json.dumps({\"energy\": energy})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Call the service to get the prediction for this time series\n",
    "prediction = service.run(energy)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " ### Plot the result\n",
    " * Convert the prediction to a data frame containing correct indices and columns.\n",
    " * Scale the original data as in the training.\n",
    " * Plot the original data and the prediction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# prediction is a string, convert it to a dictionary\n",
    "prediction = ast.literal_eval(prediction)\n",
    "\n",
    "# convert the dictionary to pandas dataframe\n",
    "prediction_df = pd.DataFrame.from_dict(prediction)\n",
    "\n",
    "prediction_df.columns = [\"load\"]\n",
    "prediction_df.index = energy_pd.iloc[2500:2510].index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scale the original data\n",
    "scaler = MinMaxScaler()\n",
    "energy_pd[\"load\"] = scaler.fit_transform(\n",
    "    np.array(energy_pd.loc[:, \"load\"].values).reshape(-1, 1)\n",
    ")\n",
    "\n",
    "# Visualize a part of the data before the forecasting\n",
    "original_data = energy_pd.iloc[1500:2501]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot the forecasted data points\n",
    "fig = plt.figure(figsize=(15, 8))\n",
    "\n",
    "plt.plot_date(\n",
    "    x=original_data.index,\n",
    "    y=original_data,\n",
    "    fmt=\"-\",\n",
    "    xdate=True,\n",
    "    label=\"original load\",\n",
    "    color=\"red\",\n",
    ")\n",
    "plt.plot_date(\n",
    "    x=prediction_df.index,\n",
    "    y=prediction_df,\n",
    "    fmt=\"-\",\n",
    "    xdate=True,\n",
    "    label=\"predicted load\",\n",
    "    color=\"yellow\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cleanup\n",
    "The service costs money during deployment. We should clean this up"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "service.delete()"
   ]
  }
 ],
 "metadata": {
  "file_extension": ".py",
  "jupytext": {
   "formats": "ipynb,py:light"
  },
  "kernel_info": {
   "name": "python3-azureml"
  },
  "kernelspec": {
   "display_name": "Python 3.6 - AzureML",
   "language": "python",
   "name": "python3-azureml"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  },
  "mimetype": "text/x-python",
  "name": "python",
  "npconvert_exporter": "python",
  "nteract": {
   "version": "nteract-front-end@1.0.0"
  },
  "pygments_lexer": "ipython3",
  "version": 3
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
